package com.runa.rccprotocol.dto.runa;

import com.runa.protocol.enums.ByteOrder;
import com.runa.protocol.util.ByteUtil;
import com.runa.rccprotocol.constant.AppFunCode;

public class RunaFrameSequence {

    public static class Control {
        public int DIR;
        public int PRM;
        public int FCB_ACD;
        public int FCV;
        public AppFunCode.prmFunCode funCode;

        public Control(byte C) {
            //传输方向，0：下行，1：上行
            DIR = C >>> 7;
            //启动标志位，0：从动站，1：启动站
            PRM = C >>> 6 & 1;
            //请求访问位，
            FCB_ACD = C >>> 5 & 1;
            //下行为帧计数有效位，上行为保留字，此处为保留字
            FCV = C >>> 4 & 1;
            //功能码
            funCode = AppFunCode.prmFunCode.valueOf(C & 0x0f);
        }

        public Control(int DIR, int PRM, int FCB_ACD, int FCV, AppFunCode.prmFunCode funCode) {
            this.DIR = DIR;
            this.PRM = PRM;
            this.FCB_ACD = FCB_ACD;
            this.FCV = FCV;
            this.funCode = funCode;
        }

        public byte getC() {
            return (byte) ((DIR << 7) + (PRM << 6) + (FCB_ACD << 5) + (FCV << 4) + (funCode.getCode() & 0x0f));
        }

    }

    public static class Area {
        public String A1;
        public int A2;
        public byte A3;

        public Area(byte[] area, int offset) {
            A1 = ByteUtil.bcd2str(area, offset, 2, false);
            A2 = ByteUtil.byteToUShort(area, offset + 2, ByteOrder.BA);
            A3 = 2;
        }

        public Area(String clientId) {
            A1 = clientId.substring(0,4);
            if (clientId.length()==8){//琅卡博
                A2 = Integer.parseInt(clientId.substring(4,8));
            }else {
                A2 = Integer.parseInt(clientId.substring(4,9));
            }
            A3 = 2; // 0000 0010
        }

        public byte[] getA() {
            byte[] area = new byte[5];
            char[] chars = A1.toCharArray();
            area[1] = (byte) ((chars[0] - 0x30<<4) | (chars[1] - 0x30)&0x0f);
            area[0] = (byte) ((chars[2] - 0x30<<4) | (chars[3] - 0x30)&0x0f);
            ByteUtil.shortToByte((short) A2, area, 2, ByteOrder.BA);
            area[4] = A3;
            return area;
        }

        @Override
        public String toString() {
            return A1+String.format("%05d", A2);
        }
    }

    public static class Seq {
        public int TpV;
        public int FIR_FIN; //00 01 10 11
        public int CON;
        public int RSEQ_PSEQ;

        public Seq(byte SEQ) {
            //帧时间标签有效位，0:附加信息域中无时间标签Tp，1:附加信息域中有时间标签Tp
            TpV = SEQ >> 7 & 1;
            //首帧、末帧标志
            FIR_FIN = (SEQ >> 5 & 2) | (SEQ >> 5 & 1);
            //请求确认标志位,0:不需要确认，1:需要确认
            CON = SEQ >> 4 & 1;
            //启动帧序号PSEQ/响应帧序号RSEQ
            RSEQ_PSEQ = (byte) (SEQ & 0x0f);
        }

        public Seq(int TpV, int FIR, int FIN, int CON, int RSEQ_PSEQ) {
            this.TpV = TpV;
            this.FIR_FIN = (FIR << 1) + FIN;
            this.CON = CON;
            this.RSEQ_PSEQ = RSEQ_PSEQ;
        }

        public byte getSeq() {
            return (byte) ((TpV << 7) + (FIR_FIN << 5) + (CON << 4) + (RSEQ_PSEQ & 0x0f));
        }

        public boolean endFrame() {
            return FIR_FIN == 3 || FIR_FIN == 1;
        }
    }

    public static class DataUnit{
        public int pn;
        public int fn;
        public byte[] data;

        public DataUnit(int pn, int fn, byte[] data) {
            this.pn = pn;
            this.fn = fn;
            this.data = data;
        }

        public byte[] getData() {
            return data == null || data.length==0
                    ? ByteUtil.merge(pnToBin(pn), fnToBin(fn))
                    : ByteUtil.merge(pnToBin(pn), fnToBin(fn), data);
        }

        public int length() {
            return 4 + (data==null||data.length==0 ? 0 : data.length);
        }
    }

    public static int pn(byte da1, byte da2) {
        da2 --;
        switch (da1){
            case 1://0000 0001
                return 1 + da2*8;
            case 2://0000 0010
                return 2 + da2*8;
            case 4://0000 0100
                return 3 + da2*8;
            case 8://0000 1000
                return 4 + da2*8;
            case 16://0001 0000
                return 5 + da2*8;
            case 32://0010 0000
                return 6 + da2*8;
            case 64://0100 0000
                return 7 + da2*8;
            case (byte) 128://1000 0000
                return 8 + da2*8;
            default:
        }
        return 0;
    }

    public static byte[] pnToBin(int pn) {
        if (pn == 0) {
            return new byte[]{0,0};
        }
        byte[] bin = new byte[2];
        int mod = pn % 8;
        if (mod == 0) {
            bin[0] = (byte) 0x80;
            bin[1] = (byte) (pn / 8);
        } else {
            bin[0] = (byte) (1 << mod - 1);
            bin[1] = (byte) (pn / 8 + 1);
        }
        return bin;
    }

    public static int fn(byte dt1, byte dt2) {
        switch (dt1){
            case 1://0000 0001
                return 1 + dt2*8;
            case 2://0000 0010
                return 2 + dt2*8;
            case 4://0000 0100
                return 3 + dt2*8;
            case 8://0000 1000
                return 4 + dt2*8;
            case 16://0001 0000
                return 5 + dt2*8;
            case 32://0010 0000
                return 6 + dt2*8;
            case 64://0100 0000
                return 7 + dt2*8;
            case (byte) 128://1000 0000
                return 8 + dt2*8;
            default:
        }
        return 0;
    }

    public static byte[] fnToBin(int fn) {
        byte[] bin = new byte[2];
        int mod = fn % 8;
        if (mod == 0) {
            bin[0] = (byte) 0x80;
            bin[1] = (byte) (fn / 8 - 1);
        } else {
            bin[0] = (byte) (1 << mod - 1); //-号优先  1左移几位
            bin[1] = (byte) (fn / 8);
        }
        return bin;
    }

    public static void main(String[] args) {
        /*byte[] bin = pnToBin(2037);
        System.out.print(bin[0]+","+bin[1]);
        System.out.println();
        bin = fnToBin(3);
        System.out.print(bin[0]+","+bin[1]);
        System.out.println();
        System.out.println(fn((byte)4, (byte)0xb));*/
//        Seq seq = newComm RunaFrameSequence.Seq(0, 1, 1, 0, 0);
//        System.out.println(seq.getSeq() & 0xff);
        System.out.println(pn((byte) 0x80, (byte) 0xa));
        System.out.println(fn((byte) 0x02, (byte) 0x16));
        System.out.println(ByteUtil.byteToHexString(fnToBin(178)));
    }

}
